#include <jni.h>
#include <string>

#include "RtpReceiver.h"
#include "RtpSender.h"

typedef struct {
    CRTPSender* sender;
    CRTPReceiver* receiver;
} RtpHandle;

static JavaVM *g_jvm;
static int m_Count;

static jlong initSender(JNIEnv *env, jclass cls, jint localPort, jstring destHost, jint destPort, jobject listener) {
    //创建Handle
    auto handle = new RtpHandle();

    //创建发送端
    auto crtpSender = new CRTPSender;

    //转换远端IP
    const char *_destHost = env->GetStringUTFChars(destHost, nullptr);

    //初始化发送端参数
    bool flag = crtpSender->initParam(g_jvm, env, crtpSender, _destHost, localPort, destPort, listener);
    //释放资源
    env->ReleaseStringUTFChars(destHost, _destHost);
    //如果初始化失败，删除资源
    if (!flag) {
        return 0;
    }
    handle->sender = crtpSender;
    return (jlong) handle;
}

static jlong initReceiver(JNIEnv *env, jclass _, jstring localHost, jint localPort, jobject listener) {
    //创建Handle
    auto handle = new RtpHandle();

    //创建接收端
    auto crtpReceiver = new CRTPReceiver;

    //转换本地IP
    const char *_localHost = env->GetStringUTFChars(localHost, nullptr);

    //初始化接收端参数
    bool flag_receive = crtpReceiver->init(g_jvm, env, crtpReceiver, _localHost, localPort, listener);
    //释放资源
    env->ReleaseStringUTFChars(localHost, _localHost);

    //如果初始化失败，删除资源
    if (!flag_receive) {
        return 0;
    }
    handle->receiver = crtpReceiver;
    return (jlong) handle;
}

static jboolean sendAvcFrame(JNIEnv *env, jclass _, jlong rtpHandler, jbyteArray data, jint length, jboolean isSpsOrPps) {
    //如果rtpHandler=0，直接返回，不处理
    if (rtpHandler == 0) {
        return (jboolean) (false);
    }
    //强转拿到rtpHandler
    auto handle = (RtpHandle *) rtpHandler;

    //如果不存在发送端，则返回
    if (handle->sender == nullptr) {
        LOGD("sender = 0 , send error!");
        return (jboolean) false;
    }

    if (m_Count % 100 == 0) {
        LOGD("RtpHandle sendByte sender addr : %p \n", handle->sender);
        m_Count = 0;
    }
    m_Count++;
    CRTPSender *crtpSender = handle->sender;
    jbyte * _data = env->GetByteArrayElements(data, nullptr);

    if (crtpSender->SendH264Nalu((unsigned char *) _data, length, isSpsOrPps)) {
        env->ReleaseByteArrayElements(data, _data, 0);
        return (jboolean) true;
    } else {
        return (jboolean) false;
    }
}

static jboolean release(JNIEnv *env, jclass _, jlong rtpHandler) {
    if (rtpHandler == 0) {
        return (jboolean) false;
    }
    auto handle = (RtpHandle *) rtpHandler;
    LOGD("%s Handle.  addr: %ld\n", "finiHandle", (long) rtpHandler);
    if (handle->sender != nullptr) {
        LOGD("%s Sender.  addr:%p\n", "finiHandle", handle->sender);
        auto crtpSender = (CRTPSender *) (handle->sender);
        if (crtpSender != nullptr) {
            crtpSender->fini();
        }
        delete crtpSender;
        handle->sender = nullptr;
    }
    if (handle->receiver != nullptr) {
        LOGD("%s Receiver.  addr:%p\n", "finiHandle", handle->receiver);
        auto crtpReceiver = (CRTPReceiver *) (handle->receiver);
        if (crtpReceiver != nullptr) {
            crtpReceiver->fini(env);
        }
        delete crtpReceiver;
        handle->receiver = nullptr;
    }
    delete handle;
    return (jboolean) true;
}

static const JNINativeMethod gMethods[] = {
        {"initSender",      "(ILjava/lang/String;ILcom/photons/jrtplib/RtpListener;)J",    (void *) initSender},
        {"initReceiver",    "(Ljava/lang/String;ILcom/photons/jrtplib/RtpListener;)J",     (void *) initReceiver},
        {"sendAvcFrame",    "(J[BIZ)Z",                                                    (void *) sendAvcFrame},
        {"release",         "(J)Z",                                                        (void *) release},
};

static int registerNativeMethods(JNIEnv* env){
    jclass clazz = env->FindClass("com/photons/jrtplib/JRTP");
    if (clazz == NULL){
        return JNI_FALSE;
    }
    if(env->RegisterNatives(clazz, gMethods, sizeof(gMethods) / sizeof(gMethods[0])) != JNI_OK){
        return JNI_FALSE;
    }
    return JNI_TRUE;
}

//回调函数
JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM* vm, void* reserved){
    JNIEnv* env = NULL;
    if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) {
        return -1;
    }
    assert(env != NULL);

    if (!registerNativeMethods(env)) {
        return -1;
    }

    return JNI_VERSION_1_6;
}
